﻿#region Namespaces

using System;
using System.Xml;
using System.Xml.Schema;

using Vibstudio.X4NET.Xml.Linq;

#endregion

namespace Vibstudio.X4NET.Xml.Schema
{
    public static class SchemaExtensions
    {
        public static IXmlSchemaInfo GetSchemaInfo(this XAttribute source)
        {
            return source.Annotation<IXmlSchemaInfo>();
        }

        public static IXmlSchemaInfo GetSchemaInfo(this XElement source)
        {
            return source.Annotation<IXmlSchemaInfo>();
        }

        public static void Validate(this XAttribute source,
                                    XmlSchemaObject partialValidationType,
                                    XmlSchemaSet schemas,
                                    ValidationEventHandler validationEventHandler)
        {
            Validate(source, partialValidationType, schemas, validationEventHandler, false);
        }

        public static void Validate(this XAttribute source,
                                    XmlSchemaObject partialValidationType,
                                    XmlSchemaSet schemas,
                                    ValidationEventHandler validationEventHandler,
                                    bool addSchemaInfo)
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }
            if (schemas == null)
            {
                throw new ArgumentNullException("schemas");
            }
            var nsmgr = new XmlNamespaceManager(new NameTable());
            var v = new XmlSchemaValidator(nsmgr.NameTable, schemas, nsmgr, XmlSchemaValidationFlags.None);
            if (validationEventHandler != null)
            {
                v.ValidationEventHandler += validationEventHandler;
            }
            if (partialValidationType != null)
            {
                v.Initialize(partialValidationType);
            }
            else
            {
                v.Initialize();
            }
            var xi = addSchemaInfo ? new XmlSchemaInfo() : null;
            v.ValidateAttribute(source.Name.LocalName, source.Name.NamespaceName, source.Value, xi);
        }

        public static void Validate(this XDocument source,
                                    XmlSchemaSet schemas,
                                    ValidationEventHandler validationEventHandler)
        {
            Validate(source, schemas, validationEventHandler, false);
        }

        public static void Validate(this XDocument source,
                                    XmlSchemaSet schemas,
                                    ValidationEventHandler validationEventHandler,
                                    bool addSchemaInfo)
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }
            if (schemas == null)
            {
                throw new ArgumentNullException("schemas");
            }
            var xrs = new XmlReaderSettings { ValidationType = ValidationType.Schema };
            xrs.Schemas = schemas;
            xrs.ValidationEventHandler += validationEventHandler;
            var xsource = new XNodeReader(source);
            var xr = XmlReader.Create(xsource, xrs);
            while (xr.Read())
            {
                if (addSchemaInfo)
                {
                    if (xr.NodeType == XmlNodeType.Element)
                    {
                        xsource.CurrentNode.AddAnnotation(xr.SchemaInfo);
                        while (xr.MoveToNextAttribute())
                        {
                            if (xr.NamespaceURI != XUtil.XmlnsNamespace)
                            {
                                xsource.GetCurrentAttribute().AddAnnotation(xr.SchemaInfo);
                            }
                        }
                        xr.MoveToElement();
                    }
                }
            }
        }

        public static OperationResult Validate(this XDocument document, string schemaUri, string targetNamespace = null)
        {
            return XsdValidator.Validate(document, schemaUri, targetNamespace);
        }

        public static OperationResult Validate(this XmlDocument document, string schemaUri, string targetNamespace = null)
        {
            return XsdValidator.Validate(document.ToXDocument(), schemaUri, targetNamespace);
        }
    }
}